import { ElMessage } from "element-plus";
class ModelFlat {
  constructor(viewer) {
    this.viewer = viewer;
    this.handler = new Cesium.ScreenSpaceEventHandler(this.viewer.scene.canvas);
    this.selectHandler = new Cesium.ScreenSpaceEventHandler(
      this.viewer.scene.canvas
    );
    this.tempLayer = new Cesium.CustomDataSource("模型压平图层");
    this.viewer.dataSources.add(this.tempLayer);
    this.curTileset; //当前的倾斜
    this.tip = null; //tip消息
    this.flatArr = []; //压平的区域数组

    this.isNeedChangeModel = false;
    this.center = null;

    this.matrix = null;
    this.localMatrix = null;
    this.localPositionsArr = [];
  }

  //开始执行压平
  async startFlat() {
    let value = 0;
    if (this.isNeedChangeModel) {
      await this.selectModel();
      this.isNeedChangeModel = false;
    }
    if (
      !this.curTileset instanceof Cesium.Cesium3DTileset ||
      !this.curTileset
    ) {
      await this.selectModel();
    }
    console.log(this.curTileset, "curTileset");
    this.center = this.curTileset.boundingSphere.center.clone();
    this.matrix = Cesium.Transforms.eastNorthUpToFixedFrame(
      this.center.clone()
    );
    this.localMatrix = Cesium.Matrix4.inverse(
      this.matrix,
      new Cesium.Matrix4()
    );
    let polygon = await this.drawPolygon();
    let obj = {
      id: `flat_${Math.random() * 100}_${polygon.positions.length}_${
        Math.random() * 100
      }`,
      positions: polygon.positions,
      height: value,
    };
    this.flatArr.push(obj);
    for (let i = 0; i < this.flatArr.length; i++) {
      let item = this.flatArr[i];
      const positions = item.positions;
      let localCoor = this.cartesiansToLocal(positions);
      this.localPositionsArr.push(localCoor);
    }

    const funstr = this.getIsinPolygonFun(this.localPositionsArr);
    let str = ``;
    for (let i = 0; i < this.localPositionsArr.length; i++) {
      const coors = this.localPositionsArr[i];
      const n = coors.length;
      let instr = ``;
      coors.forEach((coordinate, index) => {
        instr += `points_${n}[${index}] = vec2(${coordinate[0]}, ${coordinate[1]});\n`;
      });
      str += `
                ${instr}
                if(isPointInPolygon_${n}(position2D)){
                    vec4 tileset_local_position_transformed = vec4(tileset_local_position.x, tileset_local_position.y, ground_z, 1.0);
                    vec4 model_local_position_transformed = czm_inverseModel * u_tileset_localToWorldMatrix * tileset_local_position_transformed;
                    vsOutput.positionMC.xy = model_local_position_transformed.xy;
                    vsOutput.positionMC.z = model_local_position_transformed.z+ modelMC.z*0.002;
                    return;
                }`;
    }

    this.updateShader(funstr, str, value);
    this.startFlat();
  }

  //绘制面得到位置数组
  drawPolygon() {
    let pointArr = [];
    let tempPolygon = null;
    return new Promise((resolve, reject) => {
      this.handler.setInputAction((e) => {
        let pos = this.get3CFromPx(e.position);
        if (!Cesium.defined(pos)) {
          return;
        }
        let point = this.createPoint(pos);
        this.tempLayer.entities.add(point);
        pointArr.push(pos);
        if (pointArr.length == 1) {
          this.handler.setInputAction((evt) => {
            let movePos = this.get3CFromPx(evt.endPosition);
            if (!Cesium.defined(movePos)) return;
            if (Cesium.defined(tempPolygon)) {
              this.tempLayer.entities.remove(tempPolygon);
              tempPolygon = null;
            }
            tempPolygon = new Cesium.Entity({
              polygon: {
                hierarchy: new Cesium.CallbackProperty(() => {
                  return new Cesium.PolygonHierarchy(pointArr.concat(movePos));
                }, false),
                classificationType: Cesium.ClassificationType.BOTH,
                material: Cesium.Color.AQUA.withAlpha(0.5),
                heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
              },
            });
            this.tempLayer.entities.add(tempPolygon);

            this.handler.setInputAction(() => {
              this.removeEvent();
              if (tempPolygon) {
                this.tempLayer.entities.remove(tempPolygon);
                tempPolygon = null;
              }
              this.tempLayer.entities.removeAll();
              let obj = {
                id: Math.random() * 100 + "_" + Math.random() * 1000,
                name: "地形压平区域",
                positions: pointArr,
              };
              this.curFlatArea = obj.id;
              resolve(obj);
            }, Cesium.ScreenSpaceEventType.RIGHT_CLICK);
          }, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
        }
      }, Cesium.ScreenSpaceEventType.LEFT_CLICK);
    });
  }

  //拾取模型
  selectModel() {
    this.tip = ElMessage({
      type: "success",
      message: "请将鼠标放置于目标倾斜模型上！",
      duration: 4000,
      offset: 90,
    });
    return new Promise((resolve, reject) => {
      let body = document.body;
      this.selectHandler.setInputAction((evt) => {
        let obj = this.viewer.scene.pick(evt.endPosition);
        if (
          obj &&
          obj.primitive &&
          obj.primitive instanceof Cesium.Cesium3DTileset
        ) {
          this.curTileset = obj.primitive;
          obj.primitive.lightColor = new Cesium.Cartesian3(255, 204, 0);
          obj.primitive.debugShowBoundingVolume = true;

          body.style.cursor = "pointer";
          if (!this.tip) {
            this.tip = ElMessage({
              type: "success",
              message: "点击拾取此模型！",
              duration: 4000,
              offset: 90,
            });
          } else {
            this.tip.message = "点击拾取此模型！";
          }
          this.selectHandler.setInputAction((e) => {
            let objClick = this.viewer.scene.pick(e.position);
            if (
              objClick &&
              objClick.primitive &&
              objClick.primitive instanceof Cesium.Cesium3DTileset
            ) {
              this.curTileset = objClick.primitive;
              this.tip = ElMessage({
                type: "success",
                message: "拾取模型成功！",
                duration: 4000,
                offset: 90,
              });
              body.style.cursor = "default";
              setTimeout(() => {
                this.tip = ElMessage({
                  type: "success",
                  message: "左键加点，右击结束，绘制压平区域！",
                  duration: 4000,
                  offset: 90,
                });
                this.curTileset.lightColor = undefined;
                this.curTileset.debugShowBoundingVolume = false;
              }, 500);
              this.selectHandler.removeInputAction(
                Cesium.ScreenSpaceEventType.MOUSE_MOVE
              );
              this.selectHandler.removeInputAction(
                Cesium.ScreenSpaceEventType.LEFT_CLICK
              );
              resolve();
            }
          }, Cesium.ScreenSpaceEventType.LEFT_CLICK);
        } else {
          if (this.curTileset) {
            this.curTileset.lightColor = undefined;
            this.curTileset.debugShowBoundingVolume = false;
            body.style.cursor = "default";
            this.tip.message = "请将鼠标放置于目标倾斜模型上！";
          }
        }
      }, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
    });
  }

  //屏幕坐标转世界坐标
  get3CFromPx(cartesian2) {
    let pos;
    let ray = this.viewer.camera.getPickRay(cartesian2);
    pos = this.viewer.scene.globe.pick(ray, this.viewer.scene);
    return pos;
  }

  //创建点实体
  createPoint(cartesian3) {
    let point = new Cesium.Entity({
      position: cartesian3,
      point: {
        pixelSize: 5,
        color: Cesium.Color.ORANGE,
        outline: true,
        outlineWidth: 2,
        outlineColor: Cesium.Color.AQUA,
        heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
      },
    });
    return point;
  }
  //事件移除
  removeEvent() {
    this.handler.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_CLICK);
    this.handler.removeInputAction(Cesium.ScreenSpaceEventType.MOUSE_MOVE);
    this.handler.removeInputAction(Cesium.ScreenSpaceEventType.RIGHT_CLICK);
  }

  //改变压平高度
  changeFlatHeight(newHeight) {
    if (this.flatArr.length == 0) return;
    this.flatArr[this.flatArr.length - 1].height = newHeight;
    let obj = this.flatArr[this.flatArr.length - 1];
    this.removeRegionById(this.flatArr[this.flatArr.length - 1].id);
    this.addRegion(obj);
  }

  //重新拾取模型
  startNewPick(value) {
    this.curTileset = null;
    if (this.tip) {
      this.tip.close();
      this.tip = null;
    }
    this.flatArr = [];
    this.tempLayer.entities.removeAll();
    this.isNeedChangeModel = true;
    this.removeEvent();
    this.startFlat(value);
  }

  //#region ----- 压平原有代码 -----
  /**
   * @method
   */

  /**
   * 添加压平面
   * @param {Object} attr 参数
   * @param {Cesium.Cartesian3[]} attr.positions 压平面坐标
   * @param {Number} attr.height 压平深度，当前不支持单独设置
   * @param {Number} attr.id 唯一标识
   */
  addRegion(attr) {
    let { positions, height, id } = attr || {};
    if (!id)
      id = new Date().getTime() + "" + Number(Math.random() * 1000).toFixed(0);
    this.flatArr.push(attr);
    for (let i = 0; i < this.flatArr.length; i++) {
      let item = this.flatArr[i];
      const positions = item.positions;
      let localCoor = this.cartesiansToLocal(positions);
      this.localPositionsArr.push(localCoor);
    }

    const funstr = this.getIsinPolygonFun(this.localPositionsArr);
    let str = ``;
    for (let i = 0; i < this.localPositionsArr.length; i++) {
      const coors = this.localPositionsArr[i];
      const n = coors.length;
      let instr = ``;
      coors.forEach((coordinate, index) => {
        instr += `points_${n}[${index}] = vec2(${coordinate[0]}, ${coordinate[1]});\n`;
      });
      str += `
                ${instr}
                if(isPointInPolygon_${n}(position2D)){
                    vec4 tileset_local_position_transformed = vec4(tileset_local_position.x, tileset_local_position.y, ground_z, 1.0);
                    vec4 model_local_position_transformed = czm_inverseModel * u_tileset_localToWorldMatrix * tileset_local_position_transformed;
                    vsOutput.positionMC.xy = model_local_position_transformed.xy;
                    vsOutput.positionMC.z = model_local_position_transformed.z+ modelMC.z*0.002;
                    return;
                }`;
    }

    this.updateShader(funstr, str, height);
  }

  /**
   * 根据id删除压平的面
   * @param {String} id 唯一标识
   */
  removeRegionById(id) {
    if (!id) return;

    this.flatArr = this.flatArr.filter((attr) => {
      return attr.id != id;
    });

    this.localPositionsArr = [];
    for (let i = 0; i < this.flatArr.length; i++) {
      let item = this.flatArr[i];
      const positions = item.positions;
      let localCoor = this.cartesiansToLocal(positions);
      this.localPositionsArr.push(localCoor);
    }

    const funstr = this.getIsinPolygonFun(this.localPositionsArr);
    let str = ``;
    for (let i = 0; i < this.localPositionsArr.length; i++) {
      const coors = this.localPositionsArr[i];
      const n = coors.length;
      let instr = ``;
      coors.forEach((coordinate, index) => {
        instr += `points_${n}[${index}] = vec2(${coordinate[0]}, ${coordinate[1]});\n`;
      });
      str += `
                ${instr}
                if(isPointInPolygon_${n}(position2D)){
                    vec4 tileset_local_position_transformed = vec4(tileset_local_position.x, tileset_local_position.y, ground_z, 1.0);
                    vec4 model_local_position_transformed = czm_inverseModel * u_tileset_localToWorldMatrix * tileset_local_position_transformed;
                    vsOutput.positionMC.xy = model_local_position_transformed.xy;
                    vsOutput.positionMC.z = model_local_position_transformed.z+ modelMC.z*0.002;
                    return;
                }`;
    }
    this.updateShader(funstr, str);
  }

  /**
   * 销毁
   */
  destroy() {
    if (this.curTileset) {
      this.curTileset.customShader = undefined;
    }
    if (this.tip) {
      this.tip.close();
      this.tip = null;
    }
    this.curTileset = null;
    this.removeEvent();
  }

  /**
   * 根据数组长度，构建 判断点是否在面内 的压平函数
   */
  getIsinPolygonFun(polygons) {
    let pmap = polygons.map((polygon) => polygon.length);
    let uniqueArray = this.getUniqueArray(pmap);
    let str = ``;
    uniqueArray.forEach((length) => {
      str += `
                vec2 points_${length}[${length}];
                bool isPointInPolygon_${length}(vec2 point){
                int nCross = 0; // 交点数
                const int n = ${length}; 
                for(int i = 0; i < n; i++){
                    vec2 p1 = points_${length}[i];
                    vec2 p2 = points_${length}[int(mod(float(i+1),float(n)))];
                    if(p1[1] == p2[1]){
                        continue;
                    }
                    if(point[1] < min(p1[1], p2[1])){
                        continue;
                    }
                    if(point[1] >= max(p1[1], p2[1])){
                        continue;
                    }
                    float x = p1[0] + ((point[1] - p1[1]) * (p2[0] - p1[0])) / (p2[1] - p1[1]);
                    if(x > point[0]){
                     nCross++;
                    }
                }
                return int(mod(float(nCross), float(2))) == 1;
                }
            `;
    });
    return str;
  }

  updateShader(vtx1, vtx2, height) {
    let flatCustomShader = new Cesium.CustomShader({
      uniforms: {
        u_tileset_localToWorldMatrix: {
          type: Cesium.UniformType.MAT4,
          value: this.matrix,
        },
        u_tileset_worldToLocalMatrix: {
          type: Cesium.UniformType.MAT4,
          value: this.localMatrix,
        },
        u_flatHeight: {
          type: Cesium.UniformType.FLOAT,
          value: height,
        },
      },
      vertexShaderText: `
            // 所有isPointInPolygon函数
            ${vtx1}
            void vertexMain(VertexInput vsInput, inout czm_modelVertexOutput vsOutput){
                vec3 modelMC = vsInput.attributes.positionMC;
                vec4 model_local_position = vec4(modelMC.x, modelMC.y, modelMC.z, 1.0);
                vec4 tileset_local_position = u_tileset_worldToLocalMatrix * czm_model * model_local_position;
                vec2 position2D = vec2(tileset_local_position.x,tileset_local_position.y);
                // float ground_z = 0.0 + u_flatHeight;
                float ground_z = -47.0 + u_flatHeight;
                // 多个多边形区域
                ${vtx2}
            }`,
    });
    this.curTileset.customShader = flatCustomShader;
  }

  // 数组去重，不能处理嵌套的数组
  getUniqueArray = (arr) => {
    return arr.filter(function (item, index, arr) {
      //当前元素，在原始数组中的第一个索引==当前索引值，否则返回当前元素
      return arr.indexOf(item, 0) === index;
    });
  };

  // 世界坐标转数组局部坐标
  cartesiansToLocal(positions) {
    let arr = [];
    for (let i = 0; i < positions.length; i++) {
      let position = positions[i];
      let localp = Cesium.Matrix4.multiplyByPoint(
        this.localMatrix,
        position.clone(),
        new Cesium.Cartesian3()
      );
      arr.push([localp.x, localp.y]);
    }
    return arr;
  }

  //#endregion ----- 压平部分原有代码 -----
}

export default ModelFlat;
